package cn.edu.hit;

import cn.edu.hit.core.Document;
import cn.edu.hit.core.conf.ConfigureLoader;
import redis.clients.jedis.Jedis;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class CacheManager {
    private static CacheManager cacheManager;
    private final Jedis jedis;

    public static CacheManager getInstance() {
        if (cacheManager == null) {
            synchronized (CacheManager.class) {
                if (cacheManager == null) {
                    cacheManager = new CacheManager();
                }
            }
        }
        return cacheManager;
    }

    static Jedis connectJedis() {
        ConfigureLoader loader = ConfigureLoader.getInstance();
        Document conf = loader.getBaseConfigure();
        String ip = conf.getString("redis_ip", "localhost");
        String port = conf.getString("redis_port", "6379");
        String dbnum = conf.getString("redis_db", "0");
        int portNumber;
        try {
            portNumber = Integer.parseInt(port);
        } catch (Exception ex) {
            portNumber = 6379;
        }
        Jedis jedis = new Jedis(ip, portNumber);

        jedis.select(Integer.parseInt(dbnum));

//        System.out.println("select:" + dbnum);

        return jedis;
    }

    private CacheManager() {
        jedis = connectJedis();
    }

    public Jedis getJedis() {
        if(jedis.isConnected()){
            return jedis;
        }else {
            return connectJedis();
        }
    }

    public <K extends Serializable, V extends Serializable> ICachedObjectPool<K, V> createCachedObjectPool(String cacheName) {
        // return new CachedObjectPool<>(20);
        return new JedisObjectPool<>(jedis, cacheName);

    }

    private static class JedisObjectPool<K extends Serializable, V extends Serializable> implements ICachedObjectPool<K, V> {
        private Jedis jedis;
        private String cacheName;

        public JedisObjectPool(Jedis jedis, String cacheName) {
            this.jedis = jedis;
            this.cacheName = cacheName;
        }

        public static byte[] toBytes(Serializable serializable) throws IOException {
            try (ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
                 ObjectOutputStream objectOutputStream = new ObjectOutputStream(swapStream);) {
                objectOutputStream.writeObject(serializable);
                objectOutputStream.flush();
                return swapStream.toByteArray();
            }
        }

        public static Serializable fromBytes(byte[] bytes) throws IOException, ClassNotFoundException {
            try (ByteArrayInputStream swapStream = new ByteArrayInputStream(bytes);
                 ObjectInputStream objectInputStream = new ObjectInputStream(swapStream);) {
                return (Serializable) objectInputStream.readObject();
            }
        }

        @Override
        public void push(K key, V value) {
            try {
                byte[] kBytes = toBytes(key);
                byte[] vBytes = toBytes(value);
                jedis.hset(cacheName.getBytes(), kBytes, vBytes);
//                jedis.set(kBytes,vBytes);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        @Override
        public V get(K key) {
            try {
                return (V) fromBytes(jedis.hget(cacheName.getBytes(), toBytes(key)));
//                return (V) fromBytes(jedis.get(toBytes(key)));
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        public V get(K key, V defaultValue) {
            return has(key) ? get(key) : defaultValue;
        }

        @Override
        public boolean has(K key) {
            try {
                return jedis.hexists(cacheName.getBytes(), toBytes(key));
//                return jedis.exists(toBytes(key));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return false;
        }

        @Override
        public List<V> vals(K key) {
            try {
                List<V> ret = new ArrayList<>();
                List<byte[]> bytelist = jedis.hvals(toBytes(key));
                for (int i = 0; i < bytelist.size(); i++) {
                    ret.add((V) fromBytes(bytelist.get(i)));
                }
                return ret;
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
}
